/*
(c) S.A. Velastin
This shows a simple example of a server (this one) providing a "factory" of objects
In this case the objects are created by the factory and then registered with the Name Service
So, the client needs to then use the name service to locate the object (from its name)

Version 3.2
*/

#include <tao/corba.h>
#include <iostream>
#include <fstream>
#include "orbsvcs/CosNamingC.h"
#include "CommunicationS.h"
#include <list>
#include <time.h>
#include "common.h"

using namespace std;
//using namespace Reason;

static bool debug = false;                              // controls if we output messages

// This is just an example object class which is used to demonstrate that the factory can
// create instances of them
class TIDAImpl : public virtual POA_TIDA {
  private:
    ACE_Thread_Mutex    Mutex;                          // used to control access to critical parts
    CORBA::Any          StoredData;                     // this is the data held in the object
    TIDAType            IDAType;                        // what type of IDA
    bool                Empty;
    void Sleep (int us);                                // A sleeping function
    bool Acquire (int us);                              // Acquire mutex within us (else returns false)
    void Release ();
    char Name[MAXIDANAMELENGTH];
  
  public:
    // an implementation constuctor
    TIDAImpl (TIDAType IDAType, const CORBA::Any &InitialData, const char *ObjName);  
    ~TIDAImpl (void);                                   // and destructor 
    bool IsEmpty ()                                     // If the IDA has data to be retrieved
        throw(CORBA::SystemException, Could_Not_Synchronise);
    
    bool IsFull ()                                      // If the IDA has no more capacity
        throw(CORBA::SystemException, Could_Not_Synchronise);
    
    bool GetData (CORBA::Any &Data, CORBA::ULong timeout) // Gets data with a timeout in us (zero: wait)
        throw(CORBA::SystemException);
    
    bool PutData (const CORBA::Any &Data, CORBA::ULong timeout)  // Puts data  
        throw(CORBA::SystemException);
    
    TIDAType GetType()
        throw (CORBA::SystemException);                 // returns the type
    
    void say_hello(const char* name)
        throw(CORBA::SystemException);
    
    TimeOfDay get_gmt()
        throw(CORBA::SystemException);
};

//-----------------------------------------------------------------------------------------
// This is the factory
class  TIDAFactoryImpl : public virtual POA_TIDAFactory {
  private:
    ACE_Thread_Mutex    Mutex;                          // used to control access to critical parts
    CosNaming::NamingContext_var naming_context;        // set by server, it has reference to Name Service
    char Name[MAXIDANAMELENGTH];                // Our name, mainly for outputing messages to user
    
    // Here for each object we create we store sufficient information to retrieve/delete
    typedef struct {
      TIDAImpl  *TIDARef;                     // We store the pointer for later "delete"
      char       ObjName[MAXIDANAMELENGTH];   // and the name
    } TTIDAElem; 
    typedef list<TTIDAElem> TTIDAList;        // This defines a list of objects
    TTIDAList ObjList;                        // and this is the actual list
    void Sleep (int us);                      // A sleeping function
    bool Acquire (int us);                    // Acquire mutex within us (else returns false)
    void Release ();
    
  public:
    TIDAFactoryImpl (const char *name, CosNaming::NamingContext_var n_context);
    ~TIDAFactoryImpl ();
    bool CreateObject (const char* name, TIDAType IDAType, const ::CORBA::Any &InitialData)
        throw (CORBA::SystemException, Could_Not_Synchronise);
    bool RemoveObject (const char *name)
        throw (CORBA::SystemException, Could_Not_Synchronise);
    bool ExistsObject (const char *name)
        throw (CORBA::SystemException);
    bool RemoveAll ()
        throw (CORBA::SystemException, Could_Not_Synchronise);
};

//**********************************************************************************
//                              IDA
//**********************************************************************************
//------------------------------------------------------------------------------ Constuctor
TIDAImpl::TIDAImpl (TIDAType IDAT, const CORBA::Any &InitialData, const char *ObjName) {
  TImage *Image;                // this is here for testing only
  IDAType = IDAT;                                      // Sets the current data type
  if (IDAT == IDAflash) {
    if (debug) {
      if (InitialData >>= Image)
        cout << "Flash constructor " << Image->width << " " << Image->height << " " << 
            Image->widthStep << " " << Image->ImagePixels.length() << "\n";
    }
    
    StoredData = InitialData;                          // Straight copy 
    
    if (debug) {
      if (StoredData >>= Image)
        cout << "Flash Stored Data " << Image->width << " " << Image->height << " " << 
            Image->widthStep << " " << Image->ImagePixels.length() << "\n";
    }
    
    Empty = false;
  }
  else
    Empty = true;                                      // clears the stored data
  strncpy (Name, ObjName, MAXIDANAMELENGTH);           // defines its name (mainly for friendly output)
  if (debug) cout << "Returning\n";
}

//------------------------------------------------------------------------------ Destructor
TIDAImpl::~TIDAImpl (void) {
  // nothing to do yet
}

//------------------------------------------------------------------------------- Sleep
void TIDAImpl::Sleep (int us) {
  struct timespec requested, remaining;
  int seconds = us/1000000;
  int micro = us - 1000000*seconds;
  requested.tv_sec = seconds;
  requested.tv_nsec = us*1000;
  if (debug) 
    cout << "IDA '" << Name << "' Sleeping [s:us] " << seconds << ":" << micro << "\n";
  nanosleep (&requested, &remaining);
}

//------------------------------------------------------------------------------ Acquire data in critical section
bool TIDAImpl::Acquire (int us) {
  int looptimes = us/100;                           // we will try this many times
  if (debug) 
    cout << "'"<< Name << "' ¡";
  do {
    if (Mutex.tryacquire () != -1) {
      if (debug) 
        cout << "!\n";
      return true;
    }
    Sleep (100);
    looptimes--;
  } while (looptimes >= 0);
  return false;
}

//--------------------------------------------------------------------------------
//Just to ensure a message is output
void TIDAImpl::Release() {
   Mutex.release();
   if (debug) 
     cout << "IDA '" << Name << "' Released ...\n";
}
  
// Empty and Full: Because we only have one element, then Empty=there is none, Full=there is one
//------------------------------------------------------------------------------ If the buffer is empty
bool TIDAImpl::IsEmpty () throw(CORBA::SystemException, Could_Not_Synchronise)
{ bool result;
  if (debug)
    cout << "Checking IDA '" << Name << "' IsEmpty\n";
  if (Acquire(10000)) {
    if (debug)
      cout << "Empty is " << Empty << "\n";
    result = Empty;
    Release();
    return result;
  };
  throw Could_Not_Synchronise();
}  
  
//------------------------------------------------------------------------------ If the buffer is full
bool TIDAImpl::IsFull () throw(CORBA::SystemException,Could_Not_Synchronise) {
  bool result;
  if (debug) 
    cout << "Checking IDA '" << Name << "' IsFull\n";
  if (Acquire(10000)) {
    if (debug)
      cout << "Empty is " << Empty << "\n";
    result = !Empty;
    Release();
    return result;
  }
  throw Could_Not_Synchronise();
}

//------------------------------------------------------------------------------ Get the data
// Destructive read on IDAchannel and IDAsignal
bool TIDAImpl::GetData (CORBA::Any &Data, CORBA::ULong timeout) // Gets data with a timeout in us (zero: wait)
    throw(CORBA::SystemException) 
{ long looptimes = timeout/1000;
  bool alwayswait = looptimes == 0;
  TImage *Image;                // this is here for testing only
  if (debug)
    cout << "IDA '" << Name << "' GetData[" << timeout << "] Type " 
        << IDAType << " Empty " << Empty << "\n";
  do {
    if (Acquire (1000)) {                                          // get hold of the buffer
      if (!Empty) {
        // There is data in the buffer and we have control of it
        // copy the buffer's data to the output
        if (debug) {
          if (StoredData >>= Image)
            cout << "GetData, stored " 
                << "W " << Image->width << " H " << Image->height << " S "
                << Image->imageSize << " WS " <<
                Image->widthStep << " L " << Image->ImagePixels.length() << "\n";
        }
        Data = StoredData;              // THIS IS WHY Data HAS TO BE inout
        if (debug) {
          if (Data >>= Image)
            cout << "GetData, after copying " 
                << "W " << Image->width << " H " << Image->height << " S "
                << Image->imageSize << " WS " <<
                Image->widthStep << " L " << Image->ImagePixels.length() << "\n";
        }
        
        if ((IDAType == IDAchannel) || (IDAType == IDAsignal))
          Empty = true;                                            // reading empties IDA area
        //if (debug)
          cout << "GetData Success, E: " << Empty << "\n";
        Release();
        return true;
      }
      // If we get here, there was no data and we got control of the buffer
      Release();
      // And keep on waiting until we time out
    }
    Sleep (1000);                                                // Give it a chance ...
    if (!alwayswait) looptimes--;                              // we decrease if not waiting for ever
  } while (alwayswait || (looptimes >= 0));
  return false;
}
    
//------------------------------------------------------------------------------
// Destructive write on IDApool and IDAsignal (else it needs to wait)
bool TIDAImpl::PutData (const CORBA::Any &Data, CORBA::ULong timeout)  // Puts data  
        throw(CORBA::SystemException)
{ long looptimes = timeout/1000;
  bool alwayswait = looptimes == 0;
  static int count = 0;
  TImage *Image;                // this is here for testing only
  
  if (debug)
    cout << "IDA '" << Name << "' PutData[" << timeout << "] Type " 
      << IDAType << " Empty " << Empty << "\n";
  do {
    if (Acquire (1000)) {                                        // get hold of the buffer
      bool can_write;
      switch (IDAType) {                                         // not elegant or efficient but clearer
        case IDAsignal:
        case IDApool:
          can_write = true;
          break;
        case IDAflash:
          Release();
          return false;                                         // flash does not allow writing
        case IDAchannel:
          can_write = Empty;                                    // only if the buffer is not full
          break;
      }
      if (debug)
        cout << "Can write: " << can_write << "\n";
      if (can_write) {
        if (debug) {
          if (Data >>= Image)
            cout << "PutData to write: " 
                << "W " << Image->width << " H " << Image->height << " S "
                << Image->imageSize << " WS " <<
                Image->widthStep << " L " << Image->ImagePixels.length() << "\n";
        }
    

        StoredData = Data;                                      // copy data across
        if (debug) {
          if (StoredData >>= Image)
            cout << "PutData after copying " 
                << "W " << Image->width << " H " << Image->height << " S "
                << Image->imageSize << " WS " <<
                Image->widthStep << " L " << Image->ImagePixels.length() << "\n";
        }

        count++;
        //if (debug)
        Empty = false;;
        cout << "PutData Success, E: " << Empty << " " << count << "\n";
        Release();
        return true;
      }
      // If we get here, buffer was full and we got control of the buffer
      Release();
      // And keep on waiting until we time out
    }
    Sleep (1000);                                                // Give it a chance ...
    if (!alwayswait) looptimes--;                              // we decrease if not waiting for ever
  } while (alwayswait || (looptimes >= 0));
  return false;
}

//------------------------------------------------------------------------------
TIDAType TIDAImpl::GetType ()
    throw(CORBA::SystemException)
{ return IDAType;
}


//------------------------------------------------------------------------------
void TIDAImpl::say_hello(const char* name)
        throw(CORBA::SystemException)
{ cout << "IDA '" << Name << "'. You said: " << name << endl; }

//------------------------------------------------------------------------------
TimeOfDay TIDAImpl::get_gmt() throw(CORBA::SystemException)
{ time_t time_now = time(0);
  struct tm * time_p = gmtime(&time_now);

  TimeOfDay tod;
  tod.hour = time_p->tm_hour;
  tod.minute = time_p->tm_min;
  tod.second = time_p->tm_sec;
  printf("IDA '%s'. You asked for time. It is: %2.2d:%2.2d:%2.2d\n",
         Name, tod.hour,tod.minute,tod.second);
  return tod;
}

//*****************************************************************************************
//                                      FACTORY                                           *
//*****************************************************************************************
// TODO: Could use "context" in NS or the attribute "kind" in the NS
// TODO: Similar: we could use prefix using a factory name
// TODO: Best option is for the Factory to be a mini NS so that objects can only be created through here

TIDAFactoryImpl::TIDAFactoryImpl (const char *Fname, CosNaming::NamingContext_var n_context) {
  cout << "Factory '" << Fname << "' in construction\n";
  strncpy (Name, Fname, MAXIDANAMELENGTH);          // save name
  naming_context = n_context;
  
  // Register itself with the Name Service
  // Get reference to Factory object and register with Naming Service
  TIDAFactory_var Fact_obj = _this();
  // Create and initialise the name sequence
  CosNaming::Name name (1);
  name.length (1);
      // Initialise the name
  cout << "Registering factory name as '" << Name << "' ";
  name[0].id = CORBA::string_dup (Name);
  try {
    // And now register the name
    naming_context->rebind (name, Fact_obj.in ());
    cout << "Done\n";
  }
  catch (const CORBA::Exception &e) {
    cerr << "\nException on registering name " << e << endl;
  }
}

TIDAFactoryImpl::~TIDAFactoryImpl () {
  cout << "Factory '" << Name << "' destructed\n";
  RemoveAll();
}

//------------------------------------------------------------------------------- Sleep
void TIDAFactoryImpl::Sleep (int us) {
  struct timespec requested, remaining;
  int seconds = us/1000000;
  int micro = us - 1000000*seconds;
  requested.tv_sec = seconds;
  requested.tv_nsec = us*1000;
  cout << "Factory '" << Name << "' Sleeping [s:us] " << seconds << ":" << micro << "\n";
  nanosleep (&requested, &remaining);
}

//------------------------------------------------------------------------------ Acquire data in critical section
bool TIDAFactoryImpl::Acquire (int us) {
  int looptimes = us/100;                           // we will try this many times
  cout << "Factory '" << Name << "' Acquire wait " << us << ", in loops: " << looptimes << "\n";
  do {
    if (Mutex.tryacquire () != -1) {
      cout << "Factory Acquired ...\n";
      return true;
    }
    Sleep (100);
    looptimes--;
  } while (looptimes >= 0);
  return false;
}

//--------------------------------------------------------------------------------
//Just to ensure a message is output
void TIDAFactoryImpl::Release() {
  Mutex.release();
  cout << "Factory '" << Name << "' Released ...\n";
}
  

//--------------------------------------------------------------------------------- Create Object
bool TIDAFactoryImpl::CreateObject (const char *name, TIDAType IDAType, const CORBA::Any &InitialData)
    throw (CORBA::SystemException, Could_Not_Synchronise)
{ bool result = false;                  // Allows esier change of return value if code changes
  TIDAImpl *NewObject;
  TIDA_var IDA_obj;
  
  cout << "Factory '" << Name << "' creating object name '" << name << "'\n";
  
  if (ExistsObject(name)) {
    cout << "Cannot create object twice\n";
    return result;
  }
  
  // We ensure mutual exclusion on whole lot to avoid problems
  if (!Acquire(1000000)) { // allow up to a second to synchronise
    cout << "Unable to get mutex\n";
    throw Could_Not_Synchronise();
    return result;
  }
  
  NewObject = new (nothrow) TIDAImpl(IDAType, InitialData, name);      // A simple C++ create
  if (!NewObject) {
    cerr << "Factory::Unable to create the object through 'new'\n";
    Release();
    return result;
  }
  IDA_obj = NewObject->_this();             // this creates a reference to the new object
  try {
    TTIDAElem NewElement;                              // An element on the list of objects
    NewElement.TIDARef = NewObject;                    // store the C++ pointer
    strcpy (NewElement.ObjName, name);                 // and the object name
    CosNaming::Name NSname (1);                        // prepare a NS name
    NSname.length (1);
    cout << "Factory::Setting name of object to '" << name << "'\n";
    NSname[0].id = CORBA::string_dup (name);
    // And now register the name
    naming_context->rebind (NSname, IDA_obj.in ());   // register with the NS
    ObjList.push_back (NewElement);
  }
   
  catch (CORBA::Exception& e) {
    cerr << "Factory::CORBA Exception when registering object with Name Service: " << e << endl;
    delete NewObject;
    Release();
    return result;
  }
  Release();
  return true;
}

//------------------------------------------------------------------------------------ Exists Object
// This is a simple implementation. The object exists if the NS finds it.
// Of course that does not guarantee that another process did not create an object of the same name
bool TIDAFactoryImpl::ExistsObject (const char *name) 
    throw (CORBA::SystemException)
{
  CosNaming::Name NSname(1);
  NSname.length (1);
  cout << "Factory '" << Name << "' ExistsObject '" << name << "'\n";
  NSname[0].id = CORBA::string_dup (name);
  try {
    CORBA::Object_var obj = naming_context->resolve (NSname);
    cout << "Factory::Computer says 'yes'\n";
    return true;
  }
  catch (CORBA::Exception& e) {
    cout << "Factory::Computer says 'no'\n";
  }
  return false;
}
    
//----------------------------------------------------------------------------------- Remove Object
    
bool TIDAFactoryImpl::RemoveObject (const char *name)
    throw (CORBA::SystemException, Could_Not_Synchronise)
{ cout << "Factory '" << Name << "' RemoveObject '" << name << "'\n";
  if (!ExistsObject (name)) {
    cout << "Factory::RemoveObject, object does not exist\n";
    return false;
  }
  // We ensure mutual exclusion on whole lot to avoid problems
  if (!Acquire(1000000)) { // allow up to a second to synchronise
    cout << "Unable to get mutex\n";
    throw Could_Not_Synchronise();
    return false;
  }
  // Object exists with NS, so unbind it (note how we are building CosNaming inefficiently twice
  // and how just at this point object might be removed by some other process!
  CosNaming::Name NSname (1);                                           // Build the name
  NSname.length (1);
  NSname[0].id = CORBA::string_dup (name);
  try {
    naming_context->unbind (NSname);                                    // unbind the name
  }
  catch (CORBA::Exception& e) {
    cerr << "Warning! CORBA Exception: " << e << endl;                  // should not have happened
  }
  // NS entry is now unbound, we now deal with the object itself
  TTIDAList::iterator iter = ObjList.begin();  // traverse the list
  do {
    char StoredName[100];
    strcpy (StoredName, (*iter).ObjName);
    cout << "Factory::Comparing '" << StoredName << "' with '" << name << "'\n";
    if (!strcmp (StoredName, name)) {
      // Bingo! We found the name
      delete (*iter).TIDARef;                                          // delete the object
      cout << "Length before removal " << ObjList.size() << "\n";
      iter = ObjList.erase (iter);                                      // remove the element from the list
      cout << "Length after removal " << ObjList.size() << "\n";
      cout << "Factory::Object has been removed\n";                     // and keep going just in case!
    }
    else
      iter++;
  } while (iter != ObjList.end());
  Release();
  return true;
}

//------------------------------------------------------------------------------- Remove All
bool TIDAFactoryImpl::RemoveAll ()
    throw (CORBA::SystemException, Could_Not_Synchronise)
{
  TTIDAList::const_iterator iter;
  cout << "Factory '" << Name << "' RemoveAll\n";
  // We ensure mutual exclusion on whole lot to avoid problems
  if (!Acquire(1000000)) { // allow up to a second to synchronise
    cout << "Unable to get mutex\n";
    throw Could_Not_Synchronise();
    return false;
  }
  
  CosNaming::Name NSname (1);                                           // Build the name
  NSname.length (1);
  for (iter = ObjList.begin(); iter != ObjList.end(); iter++) {         // traverse the whole list
    char StoredName[100];
    strcpy (StoredName, (*iter).ObjName);
    cout << "Factory::Removing '" << StoredName << "'\n";
    NSname[0].id = CORBA::string_dup (StoredName);
    try {
      naming_context->unbind(NSname);                                   // unbind from NS
      cout << "Factory::Removed from name service\n";
    }
    catch (CORBA::Exception& e) {
      cout << "Warning! name service unbind did not work\n";
    }
    delete (*iter).TIDARef;                                            // delete the object itself
  } // for
  ObjList.clear();                                                      // we can safely clear whole list
  Release();
  return true;
}

//--------------------------------------------------------------------------------------------------------------------
//***********************************************************************************************************
int main(int argc, char* argv[])
{ TIDAFactoryImpl *hello_factory;        // we will create a factory object and register it with the NamingService

  try {
    CosNaming::NamingContext_var naming_context;
    
    CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
    // Check command line arguments.
    if (argc < 3) {
      cerr << "Usage: server <debug: 0|1> <factory_name> {<factory_name>\n";
      exit(1);
    }
    
    debug = strcmp(argv[1], "0");
    if (!debug) cout << "No ";
    cout << "debugging output\n";
    
    // Get a reference to the Root POA.
    CORBA::Object_var poa_obj = orb->resolve_initial_references("RootPOA");
    PortableServer::POA_var poa  = PortableServer::POA::_narrow(poa_obj);
    // Activate the POA manager.
    PortableServer::POAManager_var poa_mgr = poa->the_POAManager();
    poa_mgr->activate();
    
    cout << "Trying to get hold of Name Service\n";
    // Here we will get a reference to the Naming Service
    CORBA::Object_var naming_context_object = orb->resolve_initial_references ("NameService");
    naming_context = CosNaming::NamingContext::_narrow (naming_context_object.in ());
    if (naming_context)
      cerr << "Got Naming Service\n";
    else {
      cerr << "Could not find naming service\n";
      exit(1);
    }
    
    // Now create as many factories as needed
    for (int i = 2; i < argc; i++) {
      cout << "Creating factory '" << argv[i] << "'\n";
      hello_factory = new TIDAFactoryImpl(argv[i], naming_context);      // create a new factory object
      if (!hello_factory) {
        cout << "Failed to create TIDA Factory\n";
        continue;
      }
    }
    
  cout << "Ready to receive commands\n";
  orb->run();
  cout << "how did I get here?\n";
 }
 catch (CORBA::Exception& e) {
    cerr << "CORBA Exception: " << e << endl;
 }
 return 0;
}
